%{
 
#include <stdio.h>
#include <stdlib.h>
#include "treenode.h"
#include "frameq.tab.h"

int yywrap();

typedef struct _WSCount WSCount;

struct _WSCount
{
    int count;
    WSCount* next;
};

int isWSStackInitd = 0;
int line = 1;
int col = 0;
int pos = 0;

WSCount* wsStackTop = NULL;

void initWSStack();
void pushWSCount(int count);
int popWSCount();

#define MK_TOKEN_DYNAMIC(token, text) \
    yylval.tree = TreeNode_new(token, text, 1, pos, pos += yyleng, col, line); \
    col += yyleng; \
    return token;

#define MK_TOKEN_STATIC(token, text) \
    yylval.tree = TreeNode_new(token, text, 0, pos, pos += yyleng, col, line); \
    col += yyleng; \
    return token;

%}

ID                  [a-zA-Z_][a-zA-Z0-9_]*
COMMENT             #.*$
STRING_LITERAL      \"(\\\"|[^\"])*\"
TICK_STRING_LITERAL \'((\\\'|[^\']){2,})?\'
HEX_LITERAL         0(x|X)[0-9a-fA-F]+
OCT_LITERAL         0[0-7]+
INTEGER_LITERAL     [1-9][0-9]*
WS                  [ \t]+

%x COUNT_LEADING_WS REPORT_DENTATION

%%

{INTEGER_LITERAL}\.{INTEGER_LITERAL} { 
    MK_TOKEN_DYNAMIC(FLOAT_LITERAL, yytext);
}
{TICK_STRING_LITERAL} { 
    MK_TOKEN_DYNAMIC(TICK_STRING_LITERAL, yytext); 
} 

==                  { MK_TOKEN_STATIC(EQUALS, "=="); }
!=                  { MK_TOKEN_STATIC(NOT_EQUALS, "!="); }
"++"                { MK_TOKEN_STATIC(INCR, "++"); }
"--"                { MK_TOKEN_STATIC(DECR, "--"); }
"+="                { MK_TOKEN_STATIC(INCR_ASSIGN, "+="); }
"-="                { MK_TOKEN_STATIC(DECR_ASSIGN, "-="); }
"**"                { MK_TOKEN_STATIC(SCOPE, "**"); }
"->"                { MK_TOKEN_STATIC(UPSCOPE, "->"); }
new                 { MK_TOKEN_STATIC(NEW, "new"); }
true                { MK_TOKEN_STATIC(TTRUE, "true"); }
false               { MK_TOKEN_STATIC(TFALSE, "false"); }
module              { MK_TOKEN_STATIC(MODULE, "module"); }
{COMMENT}	    { pos += yyleng; col += yyleng; /* discard */ }
\'.\'               { MK_TOKEN_DYNAMIC(TICK_STRING_LITERAL, yytext); }
{STRING_LITERAL}    { MK_TOKEN_DYNAMIC(STRING_LITERAL, yytext); }
{WS}	            { pos += yyleng; col += yyleng; /* discard */ }
{INTEGER_LITERAL}   { MK_TOKEN_DYNAMIC(INTEGER_LITERAL, yytext); }
{OCT_LITERAL}       { MK_TOKEN_DYNAMIC(OCT_LITERAL, yytext); }
{HEX_LITERAL}	    { MK_TOKEN_DYNAMIC(HEX_LITERAL, yytext); }
{ID}		    { MK_TOKEN_DYNAMIC(ID, yytext); }
\n   { 
    
    line++; 
    col = 0; 

    BEGIN(COUNT_LEADING_WS);
    return NEWLINE;
}

.                       { col += 1; return yytext[0]; }

<REPORT_DENTATION>i     { return INDENT; }
<REPORT_DENTATION>d     { return DEDENT; }
<REPORT_DENTATION>u     { return UNMATCHED_INDENT; }
<REPORT_DENTATION>\*    { BEGIN(INITIAL); }

<COUNT_LEADING_WS>" "   { pos++; col++; }
<COUNT_LEADING_WS>\t    { pos++; col += 8; }
<COUNT_LEADING_WS>\n    { pos++; col=0; }

<COUNT_LEADING_WS>. {
    char c = yytext[0];
    unput(c);

    /* Pushing some fake tokens onto the stream. '*' marks the end of these. */
    unput('*');

    BEGIN(REPORT_DENTATION);

    if (c != '#' && c != '\n')
    {
        initWSStack();
        if (col == wsStackTop->count)
        {
        }
        else if (col < wsStackTop->count)
        {
            while (col < wsStackTop->count && wsStackTop->next != NULL)
            {
	        /* Dedent detected. */
	        unput('d');
                popWSCount();
            }
            if (col != wsStackTop->count)
            {
	        /* Unmatched indentation */
	        unput('u');
                printf("Error: Unmached indent level.\n");
            }
        }
        else 
        {
	    /* Indent detected. */
	    unput('i');
            pushWSCount(col);
        }
    }
}

%%

void initWSStack()
{
    if (!isWSStackInitd)
    {
        WSCount* wsc = (WSCount*)malloc(sizeof(WSCount));
        isWSStackInitd = 1;
        wsc->count = 0;
        wsc->next = NULL;
        wsStackTop = wsc;
    }
}

void pushWSCount(int count)
{
    WSCount* wsc = (WSCount*)malloc(sizeof(WSCount));
    wsc->count = count;
    wsc->next = wsStackTop;
    wsStackTop = wsc;
}

int popWSCount()
{
    int count = wsStackTop->count;

    if (wsStackTop->next != NULL)
    {
        WSCount* wsc = wsStackTop;
        wsStackTop = wsc->next;
        free(wsc);
    }
    return count;
}
